#ifndef PORTABLE_TRANSFORMER_BASE_H_
#define PORTABLE_TRANSFORMER_BASE_H_
#include <iostream>
#include <string_view>

#include "include/v8-version.h"
#include "src/ast/scopes.h"
#include "src/base/logging.h"
#include "src/codegen/compiler.h"
#include "src/codegen/script-details.h"
#include "src/execution/isolate.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/numbers/conversions.h"
#include "src/objects/elements-kind.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/fixed-array.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/literal-objects.h"
#include "src/objects/shared-function-info-inl.h"
#include "src/objects/template-objects.h"
#include "src/pbc/portable-format-helper.h"
#include "src/pbc/portable-format.h"
#include "src/pbc/transform/portable-bytecode-lower.h"

namespace v8 {
namespace internal {
using namespace portable;
// Utils
template <typename IsolateT>
inline i::Handle<i::String> UtilsInternalizeString(
    IsolateT& isolate, const std::string_view& str) {
  i::Handle<i::String> internal_str =
      isolate.factory()->InternalizeString(v8::base::Vector<const uint8_t>(
          reinterpret_cast<const uint8_t*>(str.data()), str.size()));
  return internal_str;
}

template <typename IsolateT>
inline i::Handle<i::String> UtilsInternalizeU16String(
    IsolateT& isolate, const std::u16string_view& str) {
  i::Handle<i::String> internal_str =
      isolate.factory()->InternalizeString(v8::base::Vector<const uint16_t>(
          reinterpret_cast<const uint16_t*>(str.data()), str.size()));
  return internal_str;
}

template <typename IsolateT>
inline i::Handle<i::String> UtilsInternalizeStringRef(
    const PbcScript& pbc_script, IsolateT& isolate, const StringRef& str_ref) {
  P_DCHECK(str_ref.Kind() == PbcValueKind::kU8String ||
           str_ref.Kind() == PbcValueKind::kU16String);
  if (str_ref.Kind() == PbcValueKind::kU8String) {
    std::string_view str_view = pbc_script.GetString(str_ref);
    return UtilsInternalizeString(isolate, str_view);
  } else {
    std::u16string_view str_view = pbc_script.GetU16String(str_ref);
    return UtilsInternalizeU16String(isolate, str_view);
  }
}

inline FunctionKind ConvertFunctionKind(PbcFunctionKind kind) {
  // TODO:(wwq) FIXME using mapping
  return static_cast<FunctionKind>(kind);
}

}  // namespace internal
}  // namespace v8

namespace v8 {
namespace internal {
using namespace portable;

#if V8_MAJOR_VERSION == 11
using TrustedFixedArray = FixedArray;
using TrustedByteArray = ByteArray;
#endif

class PbcTransformerBase {
 public:
  // todo(hh):refactor code for lazy so that no need filename
  explicit PbcTransformerBase(const PbcScript& pbc_script,
                              i::Isolate& v8Isolate, Handle<Script> script)
      : pbc_script_(pbc_script),
        isolate_(v8Isolate),
        zone_(isolate_.allocator(), "pbc zone"),
        ast_factory_(&zone_, isolate_.ast_string_constants(),
                     i::HashSeed(&isolate_)),
        script_(script) {}

  static constexpr char dummy_prefix_[] = "/*pbc*/";

 protected:
  virtual Handle<ScopeInfo> GetOrTransformScope(uint32_t scope_id) = 0;
  virtual Handle<SharedFunctionInfo> TransformFunction(uint32_t func_id) = 0;
  virtual Handle<BytecodeArray> TransformBytecode(uint32_t func_id) = 0;
  virtual Handle<TrustedFixedArray> TransformConstantPool(uint32_t func_id) = 0;
#if 0
  virtual Handle<TrustedByteArray> TransformExceptionTable(uint32_t func_id) = 0;
  virtual Handle<FeedbackMetadata> TransformFeedbackMeta(uint32_t func_id) = 0;
#endif

  virtual Handle<SharedFunctionInfo> GetOrCreateFuncData(uint32_t func_id) = 0;

  // init sfi immediately when GetOrCreateFuncData
  void initSharedFunctionInfo(uint32_t func_id, Handle<SharedFunctionInfo> sfi);
  Handle<Object> PbcValueToV8Value(const PbcValue& entry);
  Handle<Object> PbcShortValueToV8Value(const PbcShortValue& entry);

  Handle<FixedArray> GetOrTransformTopDeclarations();

  inline uint32_t getScriptInfosLength() {
#if V8_MAJOR_VERSION == 11
    return script_->shared_function_infos().length();
#else
    return script_->infos()->length();
#endif
  }

  // TODO: (wwq) Support transform source position table
  inline void initSourcePositionTable(Handle<BytecodeArray> bytecode_array) {
#if V8_MAJOR_VERSION == 11
    bytecode_array->set_source_position_table(
        *isolate_.factory()->empty_byte_array(), kReleaseStore);
#else
    bytecode_array->set_source_position_table(
        *isolate_.factory()->empty_trusted_byte_array(), kReleaseStore);
#endif
  }

  // pbc related
  const PbcScript& pbc_script_;

  // v8 related
  // TODO:(wwq) LocalIsolate
  Isolate& isolate_;
  Zone zone_;
  AstValueFactory ast_factory_;
  Handle<Script> script_;

 private:
  template <typename T>
  Handle<ObjectBoilerplateDescription> ToV8ObjectLiteral(T object_literal);
  Handle<TemplateObjectDescription> ToV8TemplateObject(
      PbcTemplateLiteral template_literal);
  Handle<ArrayBoilerplateDescription> ToV8ArrayLiteral(
      PbcArrayLiteral array_literal);

  Handle<FixedArray> top_declarations_;
};

}  // namespace internal
}  // namespace v8

#endif  // PORTABLE_TRANSFORMER_BASE_H_
